package work.chenxr.okhttp.utils;

import com.alibaba.fastjson2.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;

import java.rmi.ServerException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author Chenxr
 * @Date 2023/10/18
 * @Description HttpUtil
 */
@Slf4j
public class HttpUtil {
    private volatile static OkHttpClient client;

    private static final String DEFAULT_MEDIA_TYPE = "application/json";

    private static final long CONNECT_TIMEOUT = 5L;

    private static final long READ_TIMEOUT = 15L;

    /**
     * 发送GET请求
     *
     * @param url 请求地址
     * @return String
     */
    public static String doGet(String url) {
        return doGet(url, null);
    }

    /**
     * 发送GET请求
     *
     * @param url  请求地址
     * @param body 请求体
     * @return String
     */
    public static String doGet(String url, Map<?, ?> body) {
        return doGet(url, null, body);
    }

    /**
     * 发送GET请求
     *
     * @param url  请求地址
     * @param body 请求体
     * @return T
     */
    @SneakyThrows
    public static String doGet(String url, Map<String, String> headers, Map<?, ?> body) {
        // 处理请求地址，拼接参数
        if (body != null && !body.isEmpty() && !"/".equals(url.substring(url.length() - 1))) {
            StringBuilder formatUrl = new StringBuilder().append(url).append("?");

            // 循环拼接
            for (Map.Entry<?, ?> entry : body.entrySet()) {
                formatUrl.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }

            // 删除末尾的 '&' 符号
            url = formatUrl.deleteCharAt(formatUrl.length() - 1).toString();
        }

        // 创建请求
        Request.Builder requestBuilder = new Request.Builder().url(url).get();

        return doAction(url, headers, body, requestBuilder);
    }

    /**
     * 发送GET请求
     *
     * @param url  请求地址
     * @param body 请求体
     * @return String
     */
    public static String doPost(String url, Map<?, ?> body) {
        return doPost(url, null, body, DEFAULT_MEDIA_TYPE);
    }

    /**
     * 发送GET请求
     *
     * @param url       请求地址
     * @param body      请求体
     * @param mediaType 媒体类型
     * @return String
     */
    public static String doPost(String url, Map<?, ?> body, String mediaType) {
        return doPost(url, null, body, mediaType);
    }

    /**
     * 发送POST请求
     *
     * @param url       请求地址
     * @param body      请求体
     * @param mediaType 媒体类型
     * @return String
     */
    @SneakyThrows
    public static String doPost(String url, Map<String, String> headers, Map<?, ?> body, String mediaType) {
        // 媒体类型判断
        mediaType = StringUtils.isBlank(mediaType) ? DEFAULT_MEDIA_TYPE : mediaType;

        // 创建请求
        Request.Builder requestBuilder = new Request.Builder().url(url)
                .post(RequestBody.create(JSON.toJSONString(body), MediaType.parse(mediaType)));

        return doAction(url, headers, body, requestBuilder);
    }

    @SneakyThrows
    private static String doAction(String url, Map<String, String> headers, Map<?, ?> body, Request.Builder requestBuilder) {
        // 添加请求头
        if (headers != null && !headers.isEmpty()) {
            requestBuilder.headers(Headers.of(headers));
        }

        // 接受请求响应
        try (Response response = getInstance().newCall(requestBuilder.build()).execute()) {

            // 状态判断以及错误日志
            if (!response.isSuccessful() || response.body() == null) {
                log.error("http request error, url: {}, headers: {}, body: {}, msg: {}",
                        url, JSON.toJSONString(headers), JSON.toJSONString(body), response.message());
                throw new ServerException(response.message());
            }

            return response.body().string();
        }
    }

    /**
     * 双检锁单例模式获取客户端
     *
     * @return okhttp3.OkHttpClient
     */
    private static OkHttpClient getInstance() {
        if (client == null) {
            synchronized (OkHttpClient.class) {
                if (client == null) {
                    client = new OkHttpClient.Builder()
                            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                            .build();
                }
            }
        }
        return client;
    }
}
